<?php
/**
 * Store jabber stanza information
 */
abstract class Zend_Jabber_Stanza_Abstract {
    /**
     * Dom representation of jabber stanza
     *
     * @var DomDocument
     */
    protected $dom;
    
    /**
     * Stanza id
     *
     * @var integer
     */
    private $id;
    
    /**
     * Array of sent request stanzas
     *
     * @var array
     */
    private static $requests = array();
    
    /**
     * Array of recived response stanzas
     *
     * @var array
     */
    private static $responses = array();
    
    /**
     * Create new stanza 
     * 
     * For a request stanza id is generated automaticaly
     */
    public function __construct($id = false) {
        $this->id = $id !== false ? $id : md5(time());
        $this->dom = new DOMDocument();
        
        // Cache stanzas
        if(preg_match('/Request/', get_class($this))) {
            self::$requests[$this->getId()] = $this;    
        } else {
            self::$responses[$this->getId()] = $this;    
        }
    }
    
    /**
     * Get stanza id
     *
     * @return integer
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Try to guess response class from received XML
     * 
     * @param DomDocument $dom received XML wrapped in stream class
     */
    public static function getResponseClassNameByDom(DomDocument $dom) {
        $className = 'Zend_Jabber_Stanza_Response';
        
        $firstTagName = ucfirst(preg_replace('/^[^:]+:/', '', $dom->firstChild->tagName));
        
        // Special case for athorisation stanza (which are realy not stanza, but it is just so simple to
        // think about them as stanza
        if(in_array($firstTagName, array('Success', 'Failure'))) {
            $className .= '_Auth';
        }
        
        $className .= '_' . ucfirst($firstTagName);
        
        switch($firstTagName) {
            /**
             * RFC 3920 9.2.3. (IQ Semantics) sais that iq stanza should allways have a type attribute and a direct child.
             * All possible direct childs of IQ stanza shoud have a namespace identifying then (we will be using that for
             * naming responce classes). The exception only goes to error tag. It has no namespace, so we'll be using
             * error that case. 
             * The conclusion is that then the error arrived there is no reliable way to create response class name except
             * using the id to get request class and then get all it's info 
             */
            case 'Iq': 
		        $secondChild = $dom->firstChild->firstChild;
	            
	            $id = $dom->firstChild->getAttribute('id');
	            $type = $dom->firstChild->getAttribute('type');
	            
	            if($request = self::$requests[$id]) { 
		            $className .= "_" . ucfirst($request->getClass());
	            } else if($type != 'error' && !($secondChild instanceof DOMText)) {
	                $className .= "_" . ucfirst(preg_replace('/.*[#\/:_]/', '', $secondChild->getAttribute('xmlns')));
	            } else {
	                return 'Zend_Jabber_Stanza_Unknown';
	            }
	            
	            $className .= "_" . ucfirst($type);
	            
		        break;
            case 'Message':
                $type = $dom->firstChild->getAttribute('type');
		        $className .= "_" . ucfirst($type ? $type : Zend_Jabber_Stanza_Response_Message_Abstract::TYPE_NORMAL);
		        break;
        }
        
        return $className;
    }
    
    /**
     * Try to guess stanza id from received response
     * 
     * @param DomDocument $dom Received XML
     */
    public static function getStanzaIdByDom(DomDocument $dom) {
        switch($dom->firstChild->tagName) {
            case 'iq': 
                $id = $dom->firstChild->getAttribute('id'); 
                break;
            case 'message': 
                $id = $dom->getElementsByTagName('id')->item(0)->nodeValue; 
                break;
            case 'presence': 
                $id = rand(1, 99999); 
                break;
            case 'success':
            case 'failure':
		        foreach(self::$requests as $request) {
		            if(get_class($request) == $className && !$request->getResponse()) {
		                return $request->getId();
		            }
		        }
		        break;
        }
        
        return $id;
    }
    
    /**
     * Get stanza class
     * 
     * This is not actual class of the object but instead something wich identifies the 
     * class of stanza like auth, message, iq or presence
     * 
     * @param string
     */
    public function getClass() {
        preg_match('/([^_]+)_[^_]+$/', get_class($this), $match);
        return strtolower($match[1]);
    }

    /**
     * Get stanza type
     * 
     * @return string
     */
    public function getType() {
        preg_match('/[^_]+$/', get_class($this), $match);
        return strtolower($match[0]);
    }
    
    /**
     * Get stanza object as Dom
     *
     * @return DomDocument
     */
    public function toDom() {
        return $this->dom;
    }
    
    /**
     * Convert stanza DOM representation to string
     * 
     * @return string
     */
    public function toString() {
        return $this->dom->saveXML($this->dom->firstChild);
    }

    /**
     * Prints stanza object as XML tree
     * 
     * @return string
     */
    public function __toString() {
        return $this->toString();
    }
}
?>